/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.ops4j.pax.web.resources.jsf.internal;

/**
 * Utility-Methods for parameter-validation
 */
public final class ResourceValidationUtils {
	private ResourceValidationUtils() {
	}

	public static boolean isValidResourceName(String resourceName) {
		return validateResourceName(resourceName, true);
	}

	public static boolean isValidLibraryName(String libraryName) {
		return validate(libraryName, false);
	}

	public static boolean isValidLibraryName(String libraryName, boolean allowSlash) {
		return validate(libraryName, allowSlash);
	}

	public static boolean isValidResourceId(String resourceId) {
		// Follow the same rules as for resourceName, but check resourceId does not
		// start with '/'
		return validateResourceName(resourceId, true) &&
				resourceId.length() > 0 && resourceId.charAt(0) != '/';
	}

	public static boolean isValidViewResource(String resourceId) {
		// Follow the same rules as for resourceName, but check resourceId does not
		// start with '/'
		return validateResourceName(resourceId, true);
	}

	public static boolean isValidContractName(String contractName) {
		return validate(contractName, false);
	}

	public static boolean isValidLocalePrefix(String localePrefix) {
		for (int i = 0; i < localePrefix.length(); i++) {
			char c = localePrefix.charAt(i);
			if ((c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
				continue;
			} else {
				return false;
			}
		}
		return true;
	}

	private static boolean validate(String expression, boolean allowSlash) {
		if (expression.length() == 2 &&
				expression.charAt(0) == '.' &&
				expression.charAt(1) == '.') {
			return false;
		}
		for (int i = 0; i < expression.length(); i++) {
			char c = expression.charAt(i);

			// Enforce NameChar convention as specified
			// http://www.w3.org/TR/REC-xml/#NT-NameChar
			// Valid characters for NameChar
			// ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] |
			// [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] |
			// [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF]
			// | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
			// "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
			// Excluding ":"
			if ((c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') ||
					(c >= 0xC0 && c <= 0xD6) || (c >= 0xD8 && c <= 0xF6) ||
					(c >= 0xF8 && c <= 0x2FF) || (c >= 0x370 && c <= 0x37D) ||
					(c >= 0x37F && c <= 0x1FFF) || (c >= 0x200C && c <= 0x200D) ||
					(c >= 0x2070 && c <= 0x218F) || (c >= 0x2C00 && c <= 0x2FEF) ||
					(c >= 0x3001 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFDCF) ||
					(c >= 0xFDF0 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0xEFFFF) ||
					c == '-' || (c >= '0' && c <= '9') || c == 0xB7 || (c >= 0x300 && c <= 0x36F) ||
					(c >= 0x203F && c <= 0x2040) || (allowSlash && c == '/')
					) {
				continue;
			} else if (c == '.') {
				if (i + 2 < expression.length()) {
					char c1 = expression.charAt(i + 1);
					char c2 = expression.charAt(i + 2);
					if (c == c1 && (c2 == '/' || c2 == '\\')) {
						return false;
					}
				}
				continue;
			} else {
				return false;
			}
		}
		if (expression.length() >= 3) {
			int length = expression.length();
			if ((expression.charAt(length - 3) == '/' || expression.charAt(length - 3) == '\\') &&
					expression.charAt(length - 2) == '.' &&
					expression.charAt(length - 1) == '.') {
				return false;
			}
		}
		return true;
	}

	private static boolean validateResourceName(String expression, boolean allowSlash) {
		if (expression.length() == 2 &&
				expression.charAt(0) == '.' &&
				expression.charAt(1) == '.') {
			return false;
		}
		for (int i = 0; i < expression.length(); i++) {
			char c = expression.charAt(i);

			// Enforce NameChar convention as specified
			// http://www.w3.org/TR/REC-xml/#NT-NameChar
			// Valid characters for NameChar
			// ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] |
			// [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] |
			// [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF]
			// | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
			// "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
			// Excluding ":"

			// Forbidden chars by win
			// < (less than)
			// > (greater than)
			// : (colon)
			// " (double quote)
			// / (forward slash)
			// \ (backslash)
			// | (vertical bar or pipe)
			// ? (question mark)
			// * (asterisk)
			// Do not use chars in UNIX because they have special meaning
			// *&%$|^/\~
			if ((c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') ||
					(c >= 0xC0 && c <= 0xD6) || (c >= 0xD8 && c <= 0xF6) ||
					(c >= 0xF8 && c <= 0x2FF) || (c >= 0x370 && c <= 0x37D) ||
					(c >= 0x37F && c <= 0x1FFF) || (c >= 0x200C && c <= 0x200D) ||
					(c >= 0x2070 && c <= 0x218F) || (c >= 0x2C00 && c <= 0x2FEF) ||
					(c >= 0x3001 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFDCF) ||
					(c >= 0xFDF0 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0xEFFFF) ||
					(c == '-') || (c >= '0' && c <= '9') || c == 0xB7 || (c >= 0x300 && c <= 0x36F) ||
					(c >= 0x203F && c <= 0x2040) || (allowSlash && c == '/') ||
					(c == '!') || (c == '#') || (c == '\'') || (c == '(') || (c == ')') ||
					(c == '+') || (c == ',') || (c == ';') || (c == '=') ||
					(c == '@') || (c == '[') || (c == ']') || (c == '{') || (c == '}')) {
				continue;
			} else if (c == '.') {
				if (i + 2 < expression.length()) {
					char c1 = expression.charAt(i + 1);
					char c2 = expression.charAt(i + 2);
					if (c == c1 && (c2 == '/' || c2 == '\\')) {
						return false;
					}
				}
				continue;
			} else {
				return false;
			}
		}
		if (expression.length() >= 3) {
			int length = expression.length();
			if ((expression.charAt(length - 3) == '/' || expression.charAt(length - 3) == '\\') &&
					expression.charAt(length - 2) == '.' &&
					expression.charAt(length - 1) == '.') {
				return false;
			}
		}
		return true;
	}
}
